#include <stdio.h>      /* Standard input/output */
#include <fcntl.h>      /* File control definitions */
#include <string.h>
#include <stdlib.h>
#include <termio.h>     /* POSIX terminal control definitions */
#include <sys/time.h>   /* Time structures for select() */
#include <unistd.h>     /* POSIX Symbolic Constants */
#include <assert.h>
#include <errno.h>      /* Error definitions */
#include <sys/mman.h>


#define  CMSPAR 010000000000  

#define SERIAL_FLUSH_TX		1       
#define SERIAL_FLUSH_RX		2

#define SERIAL_PARITY_NO        0
#define SERIAL_PARITY_ODD       1
#define SERIAL_PARITY_EVENT     2
#define SERIAL_PARITY_MARK      3
#define SERIAL_PARITY_SPACE     4

#define SERIAL_STOPBIT_ONE      1
#define SERIAL_STOPBIT_TWO      2

static int serial_fd  = -1;

struct termios old_tios;

static void serial_print_buf(unsigned char *buf,int len) /* 打印输出 */
{
	int i;
	for(i=0;i<len;i++)
		printf("%02X ",buf[i]);
	printf("\n");
}

char *get_serial_dev_name(char *serial_name)   /* 获取串口名称 */
{ 
	if (strcasecmp(serial_name, "COM2") == 0)	/* 传入参函数是COM2(不区分大小写)，则返回串口对应的设备节点/dev/ttymxc1 */
		return "/dev/ttymxc1";
	else if (strcasecmp(serial_name, "COM3") == 0)
		return "/dev/ttymxc2";
	else if (strcasecmp(serial_name, "COM4") == 0)
		return "/dev/ttymxc3";
	else if (strcasecmp(serial_name, "COM5") == 0)
		return "/dev/ttymxc4 ";
	else
		//return NULL;
		return serial_name;
}

static int termios_init(struct termios *tios,int baud,int parity,int data_bits,int stop_bits) //串口 传入波特率，校验位，数据位，停止位。
{
	speed_t baud_rate;

	if (tios == NULL)
		return -1;

	tios->c_line  = 0;

	tios->c_cc[VMIN ] = 0;
	tios->c_cc[VTIME] = 0;

	/* configure the input modes... */
	tios->c_iflag =  IGNBRK | IGNPAR | INPCK;


	/* configure the output modes... */
	tios->c_oflag = 0;     /* enable implementation-defined output processing */
	/* configure the control modes... */
	tios->c_cflag = CREAD | CLOCAL; 
	
	if (data_bits == 5)      //对传入的数据位进行判断
		tios->c_cflag |= CS5;
	else if (data_bits == 6)
		tios->c_cflag |= CS6;
	else if (data_bits == 7)
		tios->c_cflag |= CS7;
	else if (data_bits == 8)
		tios->c_cflag |= CS8;
	else
		return -1;

	if (stop_bits == 1)		//对传入的停止位进行判断
		tios->c_cflag &=~ CSTOPB;
	else if (stop_bits == 2)
		tios->c_cflag |= CSTOPB;
	else
		return -1;

	if(parity == 0)			//校验位: 0:无校验 1:奇校验 2:偶校验 3:mark parity 4:space parity
	{ /* none */
		tios->c_cflag &=~ PARENB;
		tios->c_cflag &=~ PARODD;
	}
	else if(parity == 2)
	{ /* even */
		tios->c_cflag |= PARENB;
		tios->c_cflag &=~ PARODD;
	}
	else if(parity == 1)
	{ /* odd */
		tios->c_cflag |= PARENB;
		tios->c_cflag |= PARODD;
	}
    else if (parity == 3)/* 2008.12.17 add for support mark and space parity */
    {
        /* mark */
		tios->c_cflag |= PARENB;
		tios->c_cflag |= CMSPAR;
		tios->c_cflag |= PARODD;
    }
    else if (parity == 4)
    {
        /* space */
		tios->c_cflag |= PARENB;
		tios->c_cflag |= CMSPAR;
    }
	else
		return -1;

	/* configure the local modes... */
	tios->c_lflag = 0;    /* enable implementation-defined input processing   */

	/* Set the baud rate */
	switch(baud)			//对传入的波特率进行判断
	{
		case 110:
			baud_rate = B110;
			break;
		case 300:
			baud_rate = B300;
			break;
		case 600:
			baud_rate = B600;
			break;
		case 1200:
			baud_rate = B1200;
			break;
		case 2400:
			baud_rate = B2400;
			break;
		case 4800:
			baud_rate = B4800;
			break;
		case 9600:
			baud_rate = B9600;
			break;
		case 19200:
			baud_rate = B19200;
			break;
		case 38400:
			baud_rate = B38400;
			break;
		case 57600:
			baud_rate = B57600;
			break;
		case 115200:
			baud_rate = B115200;
			break;
		case 230400:
			baud_rate = B230400;
			break;
		case 460800:
			baud_rate = B460800;
			break;
		case 500000:
			baud_rate = B500000;
			break;
		case 576000:
			baud_rate = B576000;
			break;
		case 921600:
			baud_rate = B921600;
			break;
		case 1000000:
			baud_rate = B1000000;
			break;
		case 1152000:
			baud_rate = B1152000;
			break;
		case 1500000:
			baud_rate = B1500000;
			break;
		case 2000000:
			baud_rate = B2000000;
			break;
		case 2500000:
			baud_rate = B2500000;
			break;
		case 3000000:
			baud_rate = B3000000;
			break;
		case 3500000:
			baud_rate = B3500000;
			break;
		case 4000000:
			baud_rate = B4000000;
			break;
		default:
			return -1;
	} 
	printf("termios_init baud_rate 0x%x\n", baud_rate);
	if ((cfsetispeed(tios, baud_rate) < 0) ||(cfsetospeed(tios, baud_rate) < 0))
		return -1;
	return 0;
}

//return serial fd, error return -1
int serial_open(char *serial_name, int baud,int parity,int data_bits,int stop_bits,int timeout)  //打开串口
{
	struct termios settings;
	int fd;
	char *serial_dev_name;

	if (serial_fd >= 0)
		return 0;
	if (termios_init(&settings, baud,parity,data_bits,stop_bits) < 0)
		return 0;

	
	serial_dev_name = get_serial_dev_name(serial_name);
	if (serial_dev_name == NULL)
	{
		fprintf(stderr, "invalid serial name:%s\n", serial_name);
		return 0;
	}

	if((fd = open(serial_dev_name, O_RDWR | O_NOCTTY | O_NDELAY))< 0)
		return 0;

	if(tcgetattr(fd, &old_tios) < 0)
	{
		close(fd);
		return 0;
	}

	if(tcsetattr(fd, TCSANOW, &settings) < 0)
	{
		close(fd);
		return 0;
	}

	serial_fd = fd;
	return 1;
}

int serial_close()   	//关闭串口
{
	if (serial_fd < 0)
		return 0;
	tcsetattr(serial_fd, TCSANOW, &(old_tios));
	close(serial_fd);
	serial_fd = -1;
    
	return 1;
}

int serial_flush(int flag)
{
	if (serial_fd < 0)
		return 0;

	if (flag == SERIAL_FLUSH_TX)
		tcflush(serial_fd,TCOFLUSH);
	else if (flag == SERIAL_FLUSH_RX)
		tcflush(serial_fd,TCIFLUSH);
	else if (flag == (SERIAL_FLUSH_RX|SERIAL_FLUSH_TX) )
		tcflush(serial_fd,TCIOFLUSH);
	return 1;
}

int serial_write(char *buf,size_t size)   //串口写入数据
{
	int writesize = 0;

	writesize = write(serial_fd,buf,size);
	//printf("serial write: length=%d, packet: ",writesize);
	//serial_print_buf(buf,writesize);
	return writesize;
}

int serial_read(char *buf,size_t size)   //串口读取数据
{
	int readsize = 0;

	if (size<=0)
		return 0;

	readsize = read(serial_fd,buf,size);
	printf("serial read: length=%d, packet: ",readsize);
	serial_print_buf(buf, readsize);
	return readsize;
}


//return 1: poll ok, 0: timeout, -1: error
int serial_poll(int timeout)
{
	int fd = serial_fd;
	
	fd_set rfds;
	struct timeval tv;
	int sel_res;

	FD_ZERO(&rfds);
	FD_SET(fd, &rfds);
	tv.tv_sec = timeout/1000;
	tv.tv_usec = (timeout % 1000)*1000;
	return select(fd + 1, &rfds, NULL,NULL,&tv);
}

int main(int argc, char *argv[])
{
	int fd;
	char send_buffer[100];
	char recv_buffer[100];
	int i, ret;
	int baudrate=0;
	int packetlen;
	int interval;
	int times;
	struct timeval tv;
	fd_set rfds;
	int readBytes;
	int readBytesOneLoop;
	int readBytesAll;
	int errPackets=0;
	int byteLost=0;
	int sendBytesAll;

	if (argc != 6)    //需要传入串口对应的设备名
	{
		fprintf(stderr, "usage:serialtest com_port baudrate packetlen interval times\n");
		return -1;
	}
	sscanf(argv[2], "%d", &baudrate);
	sscanf(argv[3], "%d", &packetlen);
	if(packetlen > 16){
		fprintf(stderr, "packetlen should not more than 16\n");
		return -1;
	}
	sscanf(argv[4], "%d", &interval);
	
	sscanf(argv[5], "%d", &times);
	switch(baudrate){
		case 110:
		case 300:
		case 600:
		case 1200:
		case 2400:
		case 4800:
		case 9600:
		case 19200:
		case 38400:
		case 57600:
		case 115200:
		case 230400:
		case 460800:
		case 500000:
		case 576000:
		case 921600:
		case 1000000:
		case 1152000:
		case 1500000:
		case 2000000:
		case 2500000:
		case 3000000:
		case 3500000:
		case 4000000:
			printf("baudrate as %d for test\n", baudrate);
			break;
		default:
			fprintf(stderr, "baudrate:%d not support\n", baudrate);
			return -1;
	}

	fd = serial_open(argv[1], baudrate, SERIAL_PARITY_NO, 8, SERIAL_STOPBIT_ONE, 1000); //调用上面封装好的打开串口函数 传入设备名，波特率，校验位，数据位，停止位以及超时时间
	if (fd == 0)	//打开失败
	{
		fprintf(stderr, "open serial failed\n");
		return -1;
	}
	
	for(i=0; i<packetlen; i++)
	{
		send_buffer[i] = i;
	}	
	readBytesAll=0;
	sendBytesAll=0;
	for(i=0; i<times; i++)
	{
		serial_write(send_buffer, packetlen);  //写入数据
		sendBytesAll += packetlen;
		printf("send times %d, total bytes: %d\n", i, sendBytesAll);
		tv.tv_sec=0;
		tv.tv_usec=interval * 1000;
		select(0, NULL, NULL, NULL, &tv);
		//read
		readBytesOneLoop=0;
		while (serial_poll(1000)>0)	//当另一端传入数据时会接收到数据
		{
			readBytes=serial_read(recv_buffer, packetlen);
			readBytesOneLoop+=readBytes;
		}
		readBytesAll += readBytesOneLoop;
		if(packetlen != readBytesOneLoop){
			errPackets++;
			if(readBytesOneLoop < packetlen){
				byteLost += (packetlen - readBytesOneLoop);
			}
		}
	}
	printf("send packets %d, bytes: %d\n", times, sendBytesAll);
	printf("received bytes:%d byte lost:%d packet data err:%d\n", readBytesAll, byteLost, errPackets);
	serial_close();	
	
	return 0;
}
